/**
 * Polyline模块 - 负责地图折线的创建、管理和交互
 * 提供折线批量创建、事件绑定和实例管理功能
 */
import InfoWindow from '../InfoWindow/index'
import type { InfoWindowOptions } from '../InfoWindow/types/index'
import { coordinateConverter } from '../../utils/coordinateConverter'
import {
  StartingPointSvg,
  EndPointSvg,
  StopoverPointSvg,
  DrivingSvg,
  PointSvg,
} from './images/svg'
import PointPng from './images/point.png'
import type {
  PolylineModule,
  PolylineInstance,
  PolylineMethods,
  AnimationEvent,
  PolylineOptions,
  ExtraOptions,
  PointIcon,
  LngLat,
  LabeledPosition,
} from './types/index'

// 定义坐标转换器的键类型
type ConverterKey = keyof Omit<
  typeof coordinateConverter,
  'batchGcj02ToBd09' | 'batchBd09ToGcj02'
>

export default {
  name: 'Polyline',
  AMap: null as any,
  map: null as any,

  // 存储所有创建的折线实例
  polylineInstances: [] as PolylineInstance[],

  /**
   * 初始化方法 - 批量创建并添加折线到地图
   * @param {PolylineOptions[]} polylineOptions - 折线配置数组
   * @param {ExtraOptions} [extra={}] - 额外配置选项
   * @returns {Promise<{name: string, instance: PolylineInstance[], events: {remove: () => void, closeInfoWindow: () => void}}>}
   * @throws {TypeError} 当参数不是数组时抛出警告
   */

  // 额外配置
  extra: null as ExtraOptions | null,
  // 折线行进进度
  percent: 0,
  async init(
    polylineOptions: PolylineOptions[],
    extra: ExtraOptions = {},
  ): Promise<PolylineModule> {
    if (!this.AMap) {
      return Promise.reject(new TypeError('初始化地图失败'))
    }

    // 参数验证
    if (!Array.isArray(polylineOptions)) {
      const error = new TypeError('参数必须是折线配置数组')
      console.warn('Polyline模块: 参数验证失败', error)
      return Promise.reject(error)
    }

    // 验证数组不为空
    if (polylineOptions.length === 0) {
      return Promise.reject(new TypeError('Polyline模块: 折线配置数组为空'))
    }

    this.extra = extra

    try {
      // 加载动画插件
      await this.loadMoveAnimationPlugin()

      // 清除现有折线
      if (this.polylineInstances) {
        this.removePolylines()
      }

      // 创建所有折线实例
      this.polylineInstances = await this.createAllPolylines(polylineOptions)

      return {
        name: 'Polyline',
        instance: this.polylineInstances,
        events: {
          remove: this.removePolylines.bind(this),
          closeInfoWindow: this.closeInfoWindow.bind(this),
        },
      }
    } catch (err) {
      console.error('加载折线动画插件失败:', err)
      return Promise.reject(new TypeError(`加载折线动画插件失败:${err}`))
    }
  },

  /**
   * 加载移动动画插件
   * @returns {Promise<void>}
   */
  loadMoveAnimationPlugin(): Promise<void> {
    return new Promise((resolve, reject) => {
      this.AMap.plugin('AMap.MoveAnimation', (err?: Error) => {
        if (err) reject(err)
        else resolve()
      })
    })
  },

  /**
   * 批量创建折线实例
   * @param {PolylineOptions[]} options - 折线配置数组
   * @returns {Promise<PolylineInstance[]>} 折线实例数组的Promise
   */
  async createAllPolylines(
    options: PolylineOptions[],
  ): Promise<PolylineInstance[]> {
    // 分步处理：创建实例 -> 过滤无效值 -> 类型断言
    const polylineInstances = options
      .map(option => this.createSinglePolyline(option))
      // 使用类型守卫替代强制类型转换，确保类型安全
      .filter((instance): instance is PolylineInstance => Boolean(instance))

    return polylineInstances
  },

  // #region 创建单条折线
  /**
   * 创建单条折线
   * @param {Object} option - 折线配置
   * @param {Array} option.path - 折线路径
   * @returns {Object|null} 折线实例
   */

  createSinglePolyline(option: PolylineOptions): PolylineInstance | null {
    // 完善的参数验证
    if (!option || !Array.isArray(option.path) || option.path.length === 0) {
      console.error('Invalid option: path must be a non-empty array')
      return null
    }
    const AMap = this.AMap
    const map = this.map

    // 坐标转换

    let path = option.path

    if (
      this.extra?.coordTransform &&
      coordinateConverter[this.extra?.coordTransform]
    ) {
      // 使用类型断言和类型守卫确保类型安全
      const converterKey = this.extra.coordTransform as ConverterKey
      const converter = coordinateConverter[converterKey]

      if (typeof converter === 'function') {
        path = option.path.map(([lng, lat]) => {
          const { lng: tLng, lat: tLat } = converter({ lng, lat })
          return [tLng, tLat] as LngLat
        })
      }
    }

    // 创建起点、终点和行驶中的 marker
    const startMarker = this.createMarker(
      path[0] || [],
      this.extra?.icon?.StartPoint || {
        content: StartingPointSvg,
        offset: [-20, -40],
        zIndex: 100,
      },
    )
    const endMarker = this.createMarker(
      path[path.length - 1] || [],
      this.extra?.icon?.EndPoint || {
        content: EndPointSvg,
        offset: [-20, -40],
        zIndex: 100,
      },
    )
    const marker = this.createMarker(
      path[0] || [],
      this.extra?.icon?.Driving || {
        content: DrivingSvg,
        offset: [-9, -18],
        zIndex: 101,
      },
    )

    // 是否创建停留点
    const { stopoverMarkers, stopoverPositions } =
      this.createStopoverMarkers(option)

    // 创建标注点（函数内部会根据配置决定是否实际创建）
    this.createLabeled(path, option, {
      startMarker,
      endMarker,
      stopoverMarkers,
      stopoverPositions,
    })

    // 创建折线
    const polyline = new AMap.Polyline({
      ...option,
      map,
      path,
    })

    // 创建已通过路径的折线
    const passedPolyline = new AMap.Polyline({
      strokeColor: option.strokeColor,
      showDir: option.showDir,
      strokeWeight: option.strokeWeight,
      strokeOpacity: option.strokeOpacity || 1, //线透明度
      strokeStyle: option.strokeStyle || 'solid', //线样式
      map,
    })

    // 绑定折线控制方法
    const methods = this.bindPolylineEvents({ marker, passedPolyline }, path)

    // 创建实例对象并添加事件监听机制
    const instance: PolylineInstance = {
      polyline,
      passedPolyline,
      marker,
      methods,
      eventListeners: {
        // 目前只有折线进度事件
        percentChange: [],
      },
      /**
       * 注册事件监听器
       * @param eventName 事件名称
       * @param callback 事件回调函数
       * @returns 实例本身，支持链式调用
       */
      on: function (
        eventName: string,
        callback: (percent: number, index: number) => void,
      ): void {
        if (typeof eventName !== 'string' || typeof callback !== 'function') {
          return
        }

        if (!this.eventListeners[eventName]) {
          this.eventListeners[eventName] = []
        }

        this.eventListeners[eventName].push(callback)
      },

      /**
       * 移除事件监听器
       * @param eventName 事件名称
       * @param callback 可选，指定要移除的回调函数
       * @returns 实例本身，支持链式调用
       */
      off: function (
        eventName: string,
        callback: (percent: number, index: number) => void,
      ): void {
        if (typeof eventName !== 'string') {
          return
        }

        if (!this.eventListeners[eventName]) {
          return
        }

        if (typeof callback === 'function') {
          // 移除特定的回调函数
          const index = this.eventListeners[eventName].indexOf(callback)
          if (index !== -1) {
            this.eventListeners[eventName].splice(index, 1)
          }
        } else {
          // 清空所有回调函数
          delete this.eventListeners[eventName]
        }
      },
    }

    // 标记点移动事件
    marker.on('moving', (e: any) => {
      passedPolyline.setPath(e.passedPath)
      // this.map.setCenter(e.target.getPosition(), true);

      // 计算行驶进度
      const index = path.findLastIndex(
        (item: LngLat) =>
          item[0] === e.passedPos[0] && item[1] === e.passedPos[1],
      )
      // 已经好几轮测试了，轨迹只跑到倒数第二个点
      const percent = path.length > 2 ? (index / (path.length - 2)) * 100 : 0
      this.percent = percent

      // 触发percentChange事件
      instance.eventListeners.percentChange?.forEach(callback => {
        callback(percent, index)
      })
    })

    // 调整视图以适应路径
    this.map.setFitView(null, true)

    return instance
  },
  // #endregion 创建单条折线

  // #region 创建 marker 的公共函数
  createMarker(position: LngLat, params: PointIcon) {
    return new this.AMap.Marker({
      map: this.map,
      position,
      content: params.content || '',
      offset: params.offset
        ? new this.AMap.Pixel(params.offset[0], params.offset[1])
        : [0, 0],
      zIndex: params.zIndex || 4,
      icon: params.icon,
    })
  },
  // #endregion 创建 marker 的公共函数

  // #region 创建停留点
  createStopoverMarkers(option: PolylineOptions) {
    // 是否创建停留点
    let stopoverMarkers = <any[]>[] // 停留点标记
    let stopoverPositions = <LngLat[]>[] // 停留点经纬度， 转换后的坐标用于与path对比
    if (
      this.extra?.isStopovers &&
      option.extra?.stopovers &&
      option.extra?.stopovers.length > 0
    ) {
      stopoverPositions = option.extra.stopovers
      if (
        this.extra?.coordTransform &&
        coordinateConverter[this.extra?.coordTransform]
      ) {
        stopoverPositions = option.extra.stopovers.map(([lng, lat]: LngLat) => {
          const { lng: tLng, lat: tLat } = coordinateConverter[
            this.extra?.coordTransform as ConverterKey
          ]({ lng, lat })
          return [tLng, tLat] as LngLat
        })
      }

      stopoverMarkers = stopoverPositions.map(item => {
        return this.createMarker(
          item,
          this.extra?.icon?.StopoverPoint || {
            content: StopoverPointSvg,
            offset: [-15, -30],
          },
        )
      })
    }
    return { stopoverMarkers, stopoverPositions }
  },

  //  #endregion 创建停留点

  // #region 创建 海量点 的经过位置点位
  /**
   * 创建经过位置点位
   * @param {Array} path - 折线路径坐标
   * @param {Object} option - 折线配置选项
   * @param {Object} markers - 标记点对象，包含起点、终点、停留点标记和停留点位置
   */
  createLabeled(
    path: LngLat[],
    option: PolylineOptions,
    markers: {
      startMarker: any
      endMarker: any
      stopoverMarkers: any[]
      stopoverPositions: LngLat[]
    },
  ) {
    if (!this.extra?.isLabeled) {
      return
    }

    const { startMarker, endMarker, stopoverMarkers, stopoverPositions } =
      markers

    const labeledPosition: LabeledPosition[] = [] // 标注点位
    const seen = new Set()
    for (const i in path) {
      // 将坐标转换为字符串作为Set的键
      const key = path[i].join(',')
      if (!seen.has(key)) {
        seen.add(key)
        labeledPosition.push({
          lnglat: path[i],
          content: option?.extra?.origin ? option.extra.origin[i] : {},
        })
      }
    }

    if (!labeledPosition.length) {
      return
    }

    // 单独给起点终点创建标注
    this.bindMarkerEvents(
      startMarker,
      option?.extra?.infoWindowOpts,
      labeledPosition[0].content || '',
      labeledPosition[0].lnglat,
    )
    this.bindMarkerEvents(
      endMarker,
      option?.extra?.infoWindowOpts,
      labeledPosition[labeledPosition.length - 1].content || '',
      labeledPosition[labeledPosition.length - 1].lnglat,
    )

    // 非起止点和停留点的点位
    let massLabeledPosition: LabeledPosition[] = []

    // 停留点单独添加infoWindow
    labeledPosition.forEach((item, idx) => {
      if (idx !== 0 && idx !== labeledPosition.length - 1) {
        // 为停留点单独创建标注
        const index = stopoverPositions.findIndex(stopover => {
          return stopover[0] == item.lnglat[0] && stopover[1] == item.lnglat[1]
        })
        if (index !== -1) {
          this.bindMarkerEvents(
            stopoverMarkers[index],
            option?.extra?.infoWindowOpts,
            item.content || '',
            item.lnglat,
          )
        } else {
          massLabeledPosition.push(item)
        }
      }
    })

    // 为海量点添加infoWindow
    const mass = new this.AMap.MassMarks(massLabeledPosition, {
      opacity: 0.8,
      zIndex: 120,
      cursor: 'pointer',
      style: this.extra?.icon?.PointPng || {
        url: PointPng,
        anchor: new this.AMap.Pixel(10, 10),
        size: new this.AMap.Size(20, 20),
        zIndex: 4,
      },
    })

    const massMarker = this.createMarker(
      labeledPosition[0].lnglat,
      this.extra?.icon?.PointSvg || { content: PointSvg, offset: [-10, -10] },
    )
    mass.on('mouseover', (e: any) => {
      massMarker.setPosition(e.data.lnglat)
      this.bindMarkerEvents(
        massMarker,
        option?.extra?.infoWindowOpts,
        e.data.content || '',
        e.data.lnglat,
      )
    })

    mass.setMap(this.map)
  },
  // #endregion 创建 海量点 的经过位置点位

  // #region 绑定标记点弹窗
  /**
   * 绑定标记点交互事件
   * @param {AMap.Marker} marker - AMap标记点实例
   * @param {Object} [opts] - 信息窗口配置
   * @param {Object} [text] - 标记点文本信息
   * @param {Array} position - 标记点位置坐标
   */
  infoWindow: null as any,
  bindMarkerEvents(
    marker: any,
    opts: InfoWindowOptions,
    text: any,
    position: LngLat,
  ) {
    // 检查是否需要解除信息窗口
    if (this.extra?.relieveInfoWindow) return

    // 参数验证
    if (!marker || !position) {
      console.error('Invalid parameters: marker and position are required')
      return
    }

    // 确保 InfoWindow 已配置
    if (!InfoWindow.AMap) InfoWindow.AMap = this.AMap
    if (!InfoWindow.map) InfoWindow.map = this.map

    // 绑定点击事件
    marker.on('click', () => {
      // 避免重复创建信息窗口
      this.closeInfoWindow()

      // 只有当 opts 存在且有 content 方法时才创建信息窗口
      if (opts && typeof opts.content === 'function') {
        this.infoWindow = InfoWindow.createSingleInfoWindow(
          {
            ...opts,
            position,
            content: opts.content({ text, position }),
          },
          this.map,
        )
      }
    })
  },
  // #endregion 绑定标记点弹窗

  // #region 关闭信息窗口
  /**
   * 关闭当前打开的信息窗口
   */
  closeInfoWindow() {
    if (this.infoWindow) {
      this.infoWindow.close()
      this.infoWindow = null
    }
  },
  // #endregion 关闭信息窗口

  // #region 绑定折线控制事件
  /**
   * 绑定折线动画控制事件
   * @param instance 折线实例，仅包含marker和passedPolyline属性
   * @param path 折线路径坐标数组
   * @returns 动画控制方法对象
   */
  bindPolylineEvents(
    instance: Pick<PolylineInstance, 'marker' | 'passedPolyline'>,
    path: LngLat[],
  ): PolylineMethods {
    // 类型守卫：验证实例属性
    if (!instance.marker || !instance.passedPolyline) {
      console.error('Polyline实例缺少必要属性')
      return this.createEmptyAnimationController()
    }

    const { marker, passedPolyline } = instance
    let timer: any = null // 定时器控制行进的进度
    let idx = 0 // 记录当前路径索引

    // 验证路径是否有效
    if (!Array.isArray(path) || path.length === 0) {
      console.error('无效的路径参数: 必须是非空数组')
      return this.createEmptyAnimationController()
    }

    /** 清除定时器 */
    const clearTimer = (): void => {
      if (timer) {
        clearInterval(timer)
        timer = null
      }
    }

    /** 设置定时器 */
    const setupTimer = (duration: number): void => {
      clearTimer()
      // 确保最小步长时间为50ms
      const stepTime = Math.max(50, duration)
      timer = setInterval(() => {
        if (idx >= path.length - 1) {
          clearTimer()
        } else {
          idx++
        }
      }, stepTime)
    }

    /**
     * 开始动画
     * @param duration 动画持续时间(毫秒)
     * @param opts 动画配置项
     * @param cb 动画结束回调
     */
    const start: AnimationEvent = (
      duration = 1000,
      opts = {},
      cb?: () => void,
    ) => {
      passedPolyline.show()
      clearTimer()
      idx = 0
      setupTimer(duration)

      if (idx >= path.length - 1) {
        marker.stopMove()
        cb?.()
        return
      }

      marker.moveAlong(path, { ...opts, duration })
    }

    /** 暂停动画 */
    const pause = (): void => {
      marker.pauseMove()
      clearTimer()
    }

    /**
     * 恢复动画
     * @param duration 动画持续时间(毫秒)
     * @param opts 动画配置项
     * @param cb 动画结束回调
     */
    const resume: AnimationEvent = (
      duration = 1000,
      opts = {},
      cb?: () => void,
    ) => {
      clearTimer()
      setupTimer(duration)

      console.log(idx, path.length - 1, '索引idx')
      if (idx >= path.length - 1) {
        marker.stopMove()
        cb?.()
        return
      }

      const newPath = path.slice(idx)
      marker.moveAlong(newPath, {
        ...opts,
        duration,
      })
    }

    /** 停止动画并重置状态 */
    const stop = (): void => {
      idx = 0
      clearTimer()
      passedPolyline.hide()
      marker.stopMove()
      marker.setPosition(path[0])
      // this.map?.setCenter(path[0], true);
    }

    return { start, pause, resume, stop }
  },

  /** 创建空的动画控制器（空对象模式） */
  createEmptyAnimationController(): PolylineMethods {
    return {
      start: () => console.warn('空动画控制器: start方法未执行'),
      pause: () => console.warn('空动画控制器: pause方法未执行'),
      resume: () => console.warn('空动画控制器: resume方法未执行'),
      stop: () => console.warn('空动画控制器: stop方法未执行'),
    }
  },
  // #endregion 绑定折线控制事件

  /**
   * 移除所有折线
   * @description 清除地图上所有折线覆盖物并清理实例数组
   */
  removePolylines(): void {
    if (!this.map) {
      console.warn('Map instance is not initialized, cannot remove polylines')
      return
    }

    // 清除地图上的所有覆盖物
    this.map.clearMap()

    // 清理polylineInstances数组并停止所有标记移动
    this.polylineInstances.forEach(instance => {
      if (instance?.marker?.stopMove) {
        instance.marker.stopMove()
      }
    })
    this.polylineInstances = []
  },
}
